import {
  objectToString,
  objectToStringFormat,
  circleDuLinkedList
} from "../../shared";
import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
/**
 * 循环单链表构造器
 */
export interface CircleDuLinkedIListConstructor<T> extends IListConstructor<T> {
  new (datas: T[]): CircleDuLinkedList<T>;
  readonly [Symbol.species]: CircleDuLinkedIListConstructor<T>;
  readonly prototype: CircleDuLinkedList<T>;
  isCircleDuLinkedList<T>(obj: unknown): obj is CircleDuLinkedList<T>;
  of<T>(...datas: T[]): CircleDuLinkedList<T>;
  from<T>(datas: T[]): CircleDuLinkedList<T>;
}
/**
 * 判断是否是循环单链表
 * @param obj 任意对象
 */
export function isCircleDuLinkedList<T>(
  obj: unknown
): obj is CircleDuLinkedList<T> {
  return obj instanceof CircleDuLinkedList;
}
/**
 * 循环单链表
 */
export class CircleDuLinkedList<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return (
      objectToString(instance) === objectToStringFormat(circleDuLinkedList)
    );
  }
  static from<T>(datas: T[]): CircleDuLinkedList<T> {
    return new CircleDuLinkedList(datas);
  }
  static of<T>(...datas: T[]): CircleDuLinkedList<T> {
    return new CircleDuLinkedList(datas);
  }
  static isCircleDuLinkedList<T>(obj: unknown): obj is CircleDuLinkedList<T> {
    return isCircleDuLinkedList(obj);
  }
  static get [Symbol.species](): CircleDuLinkedIListConstructor<any> {
    return CircleDuLinkedList;
  }
  [Symbol.toStringTag] = circleDuLinkedList;
  constructor(datas: T[] = []) {
    super();
    for (const el of datas) this.add(el);
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (this._firstNode === null) {
      const node = new ListNode<T>(element);
      node.prev = node;
      node.next = node;
      this._firstNode = node;
    } else if (index === 0) {
      const next = this._firstNode;
      const node = new ListNode<T>(element, next.prev, next);
      next.prev = node;
      this._firstNode = node;
    } else if (index > this._size >> 1) {
      for (const [k, v] of this.entriesForNodeReverse())
        if (k + 1 === index) {
          const node = new ListNode<T>(element, v, v.next);
          v.next = node;
          (node.next as ListNode<T>).prev = node;
          break;
        }
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k + 1 === index) {
          const node = new ListNode<T>(element, v, v.next);
          v.next = node;
          (node.next as ListNode<T>).prev = node;
          break;
        }
    }
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    let el: T = (this._firstNode as ListNode<T>).element;
    if (this._size === 1) {
      this.clear();
    } else {
      if (index > this._size >> 1) {
        for (const [k, v] of this.entriesForNodeReverse()) {
          if (k === index) {
            (v.next as ListNode<T>).prev = v.prev;
            (v.prev as ListNode<T>).next = v.next;
            el = v.element;
          }
        }
      } else {
        for (const [k, v] of this.entriesForNode()) {
          if (k === index) {
            (v.next as ListNode<T>).prev = v.prev;
            (v.prev as ListNode<T>).next = v.next;
            el = v.element;
          }
        }
      }
      this._size--;
    }
    return el;
  }
  protected *entriesForNodeReverse(): IterableIterator<[number, ListNode<T>]> {
    let node = (this._firstNode as ListNode<T>).prev;
    let c = this._size - 1;
    while (c !== -1 && node !== null) {
      yield [c, node];
      node = node.prev;
      c--;
    }
  }
}
